#include "formulatest.h"

bool FormulaTest::run() {
    short count=0;

    if (!FormulaTest::constructorTest())
        std::cout << "ERROR " << ++count << " FormulaTest::constructorTest() faild!" << std::endl;
    if (!FormulaTest::toStringTest())
        std::cout << "ERROR " << ++count << " FormulaTest::toStringTest() faild!" << std::endl;
    if (!FormulaTest::evaluationTestRandomFormula())
        std::cout << "ERROR " << ++count << " FormulaTest::evaluationTestRandomFormula() faild!" << std::endl;
    if (!FormulaTest::evaluationTestConstructedFormula())
        std::cout << "ERROR " << ++count << " FormulaTest::evaluationTestConstructedFormula() faild!" << std::endl;
    if (!FormulaTest::mutateTest())
        std::cout << "ERROR " << ++count << " FormulaTest::mutateTest() faild!" << std::endl;
    if (!FormulaTest::deleteAndAddToFormulaTest())
        std::cout << "ERROR " << ++count << " FormulaTest::deleteAndAddToFormulaTest() faild!" << std::endl;
    if (!FormulaTest::mutateAddNewElementAtIteratorPositionTest())
        std::cout << "ERROR " << ++count << " FormulaTest::mutateAddNewElementAtIteratorPositionTest() faild!" << std::endl;
    if (!FormulaTest::mutateDeleteElementAtIteratorPositionTest())
        std::cout << "ERROR " << ++count << " FormulaTest::mutateDeleteElementAtIteratorPositionTest() faild!" << std::endl;
    if (!FormulaTest::mutateElementMutateTest())
        std::cout << "ERROR " << ++count << " FormulaTest::mutateElementMutateTest() faild!" << std::endl;
    if (!FormulaTest::toHumanReadeableStringTest())
        std::cout << "ERROR " << ++count << " FormulaTest::toHumanReadeableStringTest() faild!" << std::endl;
    if (!FormulaTest::simplificationTest())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest() faild!" << std::endl;
    if (!FormulaTest::simplificationTest2())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest2() faild!" << std::endl;
    if (!FormulaTest::simplificationTest3())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest3() faild!" << std::endl;
    if (!FormulaTest::simplificationTest4())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest4() faild!" << std::endl;
    if (!FormulaTest::simplificationTest5())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest5() faild!" << std::endl;
    if (!FormulaTest::simplificationTest6())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTest6() faild!" << std::endl;
    if (!FormulaTest::simplificationTestRandomFormula())
        std::cout << "ERROR " << ++count << " FormulaTest::SimplificationTestRandomFormula() faild!" << std::endl;

    return count==0;
}

bool FormulaTest::constructorTest() {
    Formula *testFormula = new Formula();
    delete testFormula;
    return true;
}

bool FormulaTest::toStringTest() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& felist = testFormula->getListOfFormulaElements();
    felist.addAtStart(*(new RealNumber(749.12)));
    felist.addAtStart(*(new RealNumber(843.18)));
    felist.addAtEnd(*(new RealPlus()));
    felist.addAtEnd(*(new RealNumber(343.234)));
    felist.addAtEnd(*(new RealDivide()));
    felist.addAtEnd(*(new RealVariable('n',11)));
    felist.addAtEnd(*(new RealMultiplication()));
    std::string& temp = testFormula->toString();
    assert(!temp.compare("843.18 749.12 + 343.234 / n *"));
    delete &temp;
    delete testFormula;
    return true;
}

bool FormulaTest::evaluationTestRandomFormula() {
    unsigned int size = 11;
    Formula *testFormula = FormulaFactory::createNewFormula(size ,false);
    Input input;
    input.createPrimeInput(60);
    InputIterator& inputIterator = input.getNewIterator();
    inputIterator.reset();
    unsigned int s=testFormula->getSize();
    testFormula->evaluate(inputIterator);
    double z=testFormula->getFitness();
    inputIterator.reset();
        delete &inputIterator;
        InputIterator& inputIterator2 = input.getNewIterator();
    assert(testFormula->getFitness()==z);
    if(testFormula->wasNeverNan) {
        testFormula->evaluate(inputIterator2);
        inputIterator2.reset();
        testFormula->evaluate(inputIterator2);
        inputIterator2.reset();
        testFormula->evaluate(inputIterator2);
        inputIterator2.reset();
        testFormula->evaluate(inputIterator2);
        inputIterator2.reset();
        testFormula->evaluate(inputIterator2);
        inputIterator2.reset();
    }
    if(z==z){
        assert(testFormula->getSize()==s);
        assert(testFormula->getFitness()==z);
    }
    delete &inputIterator2;
    delete testFormula;
    return true;
}

bool FormulaTest::evaluationTestConstructedFormula() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(78)));
    list.addAtEnd(*(new RealNumber(67)));
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealMinus()));
    list.addAtEnd(*(new RealNumber(35)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealPow()));
    list.addAtEnd(*(new RealNumber(2)));
    list.addAtEnd(*(new RealDivide()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealPlus()));

    Input input;
    input.createPrimeInput(2);
    InputIterator& inputIterator = input.getNewIterator();
    testFormula->evaluate(inputIterator);

    assert(testFormula->getFitness()==(76+testFormula->getSize()));
    inputIterator.reset();
    testFormula->evaluate(inputIterator);
    assert(testFormula->getFitness()==(76+testFormula->getSize()));

    delete &inputIterator;
    delete testFormula;
    return true;
}

bool FormulaTest::mutateTest() {
    unsigned int size = 11;
    Formula *testFormula = FormulaFactory::createNewFormula(size ,false);
    Input input;
    input.createPrimeInput(6);
    InputIterator& inputIterator = input.getNewIterator();

    for(int i=20; --i;) {
        testFormula->mutate();
//        std::cout<<(testFormula->toString())<<"\n";
    }

    testFormula->evaluate(inputIterator);
    delete &inputIterator;
    delete testFormula;
    return true;
    return false;
}

bool FormulaTest::deleteAndAddToFormulaTest() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(78)));
    list.addAtEnd(*(new RealNumber(67)));
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealMinus()));
    list.addAtEnd(*(new RealPlus()));

    list.resetIteratorPosition();
    list.iteratorGetNext();
    list.iteratorGetNext();
    list.deleteAtIteratorPosition();
    list.addAtIteratorPosition(*(new RealNumber(56)));

    Input input;
    input.createPrimeInput(1);
    InputIterator& inputIterator = input.getNewIterator();
    testFormula->evaluate(inputIterator);

    assert(testFormula->getFitness()==(42+testFormula->getSize()));

    delete &inputIterator;
    delete testFormula;
    return true;
}

bool FormulaTest::mutateAddNewElementAtIteratorPositionTest() {
    unsigned int size = 11;
    Formula *testFormula = FormulaFactory::createNewFormula(size ,false);
    Input input;
    input.createPrimeInput(6);
    InputIterator& inputIterator = input.getNewIterator();

    LinkedList<FormulaElement>& elements=testFormula->getListOfFormulaElements();

    int chance=5;
    int level=0;
    elements.resetIteratorPosition();
    testFormula->mutateAddNewElementAtIteratorPosition(chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(--chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(--chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(--chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(--chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateAddNewElementAtIteratorPosition(++chance, level, elements.iteratorPositionIsCloseToStart());
    testFormula->mutateBringLevelBackToZero(level);

    testFormula->evaluate(inputIterator);
    delete &inputIterator;
    delete testFormula;
    return true;
}

bool FormulaTest::mutateDeleteElementAtIteratorPositionTest() {
    unsigned int size = 11;
    Formula *testFormula = FormulaFactory::createNewFormula(size ,false);
    Input input;
    input.createPrimeInput(6);
    InputIterator& inputIterator = input.getNewIterator();

    int chance=5;
    int level=0;
    testFormula->getListOfFormulaElements().resetIteratorPosition();
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateAddNewElementAtIteratorPosition(chance, level, testFormula->getListOfFormulaElements().iteratorPositionIsCloseToStart());
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateDeleteElementAtIteratorPosition(chance, level);
    testFormula->mutateBringLevelBackToZero(level);

    testFormula->evaluate(inputIterator);
    delete &inputIterator;
    delete testFormula;
    return true;
}

bool FormulaTest::mutateElementMutateTest() {
    unsigned int size = 11;
    Formula *testFormula = FormulaFactory::createNewFormula(size ,false);
    Input input;
    input.createPrimeInput(6);
    InputIterator& inputIterator = input.getNewIterator();

    testFormula->getListOfFormulaElements().resetIteratorPosition();
    while(testFormula->getListOfFormulaElements().iteratorHasNext())
        testFormula->mutateElementMutate();
//    testFormula->mutateBringLevelBackToZero(level);

    testFormula->evaluate(inputIterator);
    delete &inputIterator;
    delete testFormula;
    return true;
}

bool FormulaTest::toHumanReadeableStringTest() {
    unsigned int& length = *(new unsigned int(15));
    Formula& formula = *(FormulaFactory::createNewFormula(length,false));
    std::string& humanString = formula.toHumanString();
    std::string& toString = formula.toString();
//    std::cout   << std::endl
//                << humanString
//                << std::endl
//                << toString
//                << std::endl;
    delete &humanString;
    delete &toString;
    delete &length;
    delete &formula;
    return true;
}

bool FormulaTest::simplificationTest() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(78)));
    list.addAtEnd(*(new RealNumber(67)));
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealMinus()));
    list.addAtEnd(*(new RealNumber(35)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealPow()));
    list.addAtEnd(*(new RealNumber(2)));
    list.addAtEnd(*(new RealDivide()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealMultiplication()));

    testFormula->simplify();

    delete testFormula;
    return true;
}

bool FormulaTest::simplificationTest2() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealMinus()));
    testFormula->simplify();
    std::string &tempString=testFormula->toString();
    assert(tempString.compare("11")==0);
    delete &tempString;
    delete testFormula;
    return true;
}

bool FormulaTest::simplificationTest3() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealMultiplication()));
    list.addAtEnd(*(new RealNumber(5)));
    list.addAtEnd(*(new RealDivide()));
    testFormula->simplify();
    std::string &tempString=testFormula->toString();
    assert(tempString.compare("504")==0);
    delete &tempString;
    delete testFormula;
    return true;
}

bool FormulaTest::simplificationTest5() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(5)));
    list.addAtEnd(*(new RealNumber(3)));
    list.addAtEnd(*(new RealPow()));
    list.addAtEnd(*(new RealNumber(5)));
    list.addAtEnd(*(new RealDivide()));
    testFormula->simplify();
    std::string &tempString=testFormula->toString();
    assert(tempString.compare("25")==0);
    delete &tempString;
    delete testFormula;
    return true;
}

bool FormulaTest::simplificationTest4() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealNumber(78)));
    list.addAtEnd(*(new RealNumber(67)));
    list.addAtEnd(*(new RealNumber(56)));
    list.addAtEnd(*(new RealNumber(45)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealMinus()));
    list.addAtEnd(*(new RealNumber(35)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealPow()));
    list.addAtEnd(*(new RealNumber(2)));
    list.addAtEnd(*(new RealDivide()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealMultiplication()));
    list.addAtEnd(*(new RealMultiplication()));

    testFormula->simplify();
    std::string &tempString=testFormula->toString();
    assert(tempString.compare("101 39 n + n * *")==0);
    delete &tempString;

    delete testFormula;
    return true;
}

bool FormulaTest::simplificationTestRandomFormula() {
    unsigned int s=11;
    Formula *randomFormula = FormulaFactory::createNewFormula(s,false);
    Input input;
    input.createPrimeInput(12);
    InputIterator& inputIterator = input.getNewIterator();
    inputIterator.reset();
    randomFormula->evaluate(inputIterator);
    double test=randomFormula->getFitness()-randomFormula->getSize();
    inputIterator.reset();
    if(randomFormula->wasNeverNan)
        randomFormula->evaluate(inputIterator);
    std::string &tempString=randomFormula->toString();
    std::string &tempStringH=randomFormula->toHumanString();
    std::cout<< tempString<< "   <=>   " << tempStringH <<std::endl;
    delete &tempString;
    delete &tempStringH;
    randomFormula->simplify();
    std::string &tempString2=randomFormula->toString();
    std::string &tempStringH2=randomFormula->toHumanString();
    std::cout<< tempString2<< "   <=>   " << tempStringH2 <<std::endl;
    delete &tempString2;
    delete &tempStringH2;
    inputIterator.reset();
    if(randomFormula->wasNeverNan)
        randomFormula->evaluate(inputIterator);
    if(test==test) {
        if(test>0) {
            assert((randomFormula->getFitness()-randomFormula->getSize())>(test*0.995));
            assert((randomFormula->getFitness()-randomFormula->getSize())<(test*1.005));
        }
        else {
            assert((randomFormula->getFitness()-randomFormula->getSize())>(test*1.005));
            assert((randomFormula->getFitness()-randomFormula->getSize())<(test*0.995));
        }
    }
    delete &inputIterator;
    delete randomFormula;
    return true;
}

bool FormulaTest::simplificationTest6() {
    Formula *testFormula = new Formula();
    LinkedList<FormulaElement>& list = testFormula->getListOfFormulaElements();
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealNumber(12.2454)));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealMultiplication()));
    list.addAtEnd(*(new RealPow()));
    list.addAtEnd(*(new RealVariable('n', 0)));
    list.addAtEnd(*(new RealNumber(18.5045)));
    list.addAtEnd(*(new RealDivide()));
    list.addAtEnd(*(new RealPlus()));
    list.addAtEnd(*(new RealPlus()));

//    std::string &tempString2 = testFormula->toString();
//    std::cout<<tempString2<<std::endl;
//    delete &tempString2;

    testFormula->simplify();
    std::string &tempString=testFormula->toString();
//    std::cout << tempString <<std::endl;
    assert(tempString.compare("n 12.2454 n 2 ^ ^ n 18.5045 / + +")==0);
    delete &tempString;

    delete testFormula;
    return true;
}
